#!/usr/bin/env bash
#
# Script for generating a Rust test file that verifies all bitcoin_primitives
# items are re-exported in the `bitcoin` crate.
#
# The script parses api/primitives/all-features.txt and generates use statements
# that will fail to compile if any re-exports are missing.

set -euo pipefail

api_file="./api/primitives/all-features.txt"
output_file="./bitcoin/tests/check-re-exports.rs"

usage() {
    cat <<EOF
Usage:

    ./generate-bitcoin-re-export-test.sh

DESCRIPTION
  Generates a Rust test file that verifies all public types and modules from
  bitcoin_primitives are re-exported in the bitcoin crate;

  The script parses api/primitives/all-features.txt and creates use statements for
  every 'pub enum', 'pub struct', and 'pub mod' declaration.

  Output file: bitcoin/tests/check-re-exports.rs
EOF
}

main() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help)
                usage
                exit 0
                ;;
            *)
                say_err "unknown option: $1"
                usage
                exit 1
                ;;
        esac
    done

    check_required_commands
    check_required_files

    say "Parsing $api_file and generating Rust test..."

    generate_test_file

    say "Generated $output_file"
    say "Run 'cd bitcoin && cargo test --all-features check_all_bitcoin_primitives_items_are_reexported' to test"
}

generate_test_file() {
    local temp_file;
    temp_file=$(mktemp)

    # Generate the file header
    cat > "$temp_file" <<'EOF'
// SPDX-License-Identifier: CC0-1.0

//! Test that all public types and modules from bitcoin_primitives are re-exported in the bitcoin crate.
//!
//! This test is automatically generated by contrib/generate-bitcoin-re-export-test.sh
//! Any compilation error indicates a missing re-export.

#![allow(dead_code)]
#![allow(unused_imports)]
// No benefit in running this test without features enabled.
#[cfg(not(feature = "arbitrary"))]
compile_error!("arbitrary feature needs to be enabled");

#[test]
fn check_all_bitcoin_primitives_items_are_reexported() {
    // This test will fail to compile if any bitcoin_primitives item is not re-exported in the bitcoin crate.

EOF

    # Extract and convert all pub items
    local use_statements=()
    local seen_items=()

    while IFS= read -r line; do
        local path=""

        # Extract pub enum
        if [[ "$line" =~ pub\ enum\ (bitcoin_primitives::[^[:space:]]+) ]]; then
            path="${BASH_REMATCH[1]}"
        # Extract pub struct
        elif [[ "$line" =~ pub\ struct\ (bitcoin_primitives::[^[:space:]\(]+) ]]; then
            path="${BASH_REMATCH[1]}"
        # Extract pub mod
        elif [[ "$line" =~ ^pub\ mod\ (bitcoin_primitives::[^[:space:]]+)$ ]]; then
            path="${BASH_REMATCH[1]}"
        fi

        if [[ -n "$path" ]]; then
            # Remove generic type parameters (e.g., <T>)
            path="${path%%<*}"

            if [[ "$path" == *Encoder* ]] || [[ $path == *Decoder* ]]; then
                continue
            fi

            # Convert bitcoin_primitives:: to bitcoin::
            local bitcoin_path="${path//bitcoin_primitives::/bitcoin::}"

            # Skip if we've already seen this item
            if [[ " ${seen_items[*]} " != *" $bitcoin_path "* ]]; then
                seen_items+=("$bitcoin_path")
                use_statements+=("    use $bitcoin_path as _;")
            fi
        fi
    done < "$api_file"

    # Sort use statements and add to file
    printf '%s\n' "${use_statements[@]}" | sort >> "$temp_file"

    # Add closing brace
    echo "}" >> "$temp_file"

    # Move temp file to final location
    mv "$temp_file" "$output_file"
}

check_required_files() {
    if [[ ! -f "$api_file" ]]; then
        err "Required file not found: $api_file"
    fi

    local output_dir
    output_dir=$(dirname "$output_file")
    if [[ ! -d "$output_dir" ]]; then
        err "Output directory not found: $output_dir"
    fi
}

check_required_commands() {
    need_cmd grep
    need_cmd sort
    need_cmd mktemp
}

say() {
    echo "generate-bitcoin-re-export-test: $1"
}

say_err() {
    say "$1" >&2
}

err() {
    echo "$1" >&2
    exit 1
}

need_cmd() {
    if ! command -v "$1" > /dev/null 2>&1
    then err "need '$1' (command not found)"
    fi
}

#
# Main script
#
main "$@"
exit 0
